#include "StdAfx.h"
#include "GalconRoom.h"
#include <boost/make_shared.hpp>
#include <boost/foreach.hpp>

struct ParticipantFilter
{
public:
   ParticipantFilter(ServerParticipantPtr participant, const Message& msg)
      : m_participant(participant)
      , m_msg(msg)
   {

   }

   void operator ()(ServerParticipantPtr participant) 
   {
      if (participant != m_participant)
      {
         participant->Deliver(m_msg);
      }
   }

private:
   ServerParticipantPtr m_participant;
   const Message& m_msg;
};

// count of participants that is needed to start game
unsigned int maxCountOfParticipants = 2;
unsigned int countOfPlanets = 10;

GalconRoom::GalconRoom(boost::asio::io_service& ioService): m_ioService(ioService)
{
   m_gameStateAccessor = boost::make_shared<GameStateAccessor>(countOfPlanets, maxCountOfParticipants);
}

GalconRoom::~GalconRoom()
{
}

void GalconRoom::handle_timer(const boost::system::error_code &error)
{
   m_gameStateAccessor->UpdatePlanets();
   Deliver(*m_gameStateAccessor->GetGameStateMsg());
   m_timer->expires_at(m_timer->expires_at() + boost::posix_time::seconds(1));
   m_timer->async_wait(boost::bind(&GalconRoom::handle_timer, this, boost::asio::placeholders::error));
}

void GalconRoom::Join( ServerParticipantPtr participant )
{
   if (m_participants.size() < maxCountOfParticipants)
   {
      m_participants.insert(participant);
      
      doUpdateGameState();
   }
   else
   {
      sendRejectionMessage(participant, CountOfParticipantsIsMax);
   }
}

void GalconRoom::Deliver( const Message& msg )
{
   traceMessage(msg, "GalconRoom::Deliver");
   std::for_each(m_participants.begin(), m_participants.end(),
      boost::bind(&ServerParticipant::Deliver, _1, boost::ref(msg)));
}

void GalconRoom::Deliver( const Message& msg, ServerParticipantPtr participant )
{
   ParticipantFilter filter(participant, msg);
   std::for_each(m_participants.begin(), m_participants.end(),
      filter);
}

void GalconRoom::startGame()
{
   PlayerID currentParticipant = 0;
   BOOST_FOREACH(const ServerParticipantPtr& participant, m_participants)
   {
      participant->Deliver(*m_gameStateAccessor->GetInitialGameStateMsg(currentParticipant, GameColors(currentParticipant)));
      currentParticipant++;
   }
   m_timer.reset(new boost::asio::deadline_timer(m_ioService, boost::posix_time::seconds(1)));
   m_timer->async_wait(boost::bind(&GalconRoom::handle_timer, this, boost::asio::placeholders::error));
}

void GalconRoom::sendRejectionMessage(ServerParticipantPtr participant, RejectionReason rejectionReason)
{
   MessagePtr message = MessageBuilder::BuildRejectionMessage(rejectionReason);
   participant->Deliver(*message);
}

void GalconRoom::traceMessage( const Message& msg, const std::string& info)
{
   std::string m(msg.GetBody(), msg.GetBodyLength());
   std::cout << info << m << '\n';
}

void GalconRoom::doUpdateGameState()
{
   if (m_participants.size() == maxCountOfParticipants && !m_timer)
   {
      startGame();
   }
   if (m_participants.size() <= 1)
   {
      restartGame();
   }
}

void GalconRoom::Leave( ServerParticipantPtr participant )
{
   m_participants.erase(participant);
   
   doUpdateGameState();
}